/*******************************************************************************
* Copyright (c) 2006-2013
* Software Technology Group, Dresden University of Technology
* DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Software Technology Group - TU Dresden, Germany;
* DevBoost GmbH - Berlin, Germany
* - initial API and implementation
******************************************************************************/
package org.emftext.language.java.ejava.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.BasicEMap;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypeParameter;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.emftext.language.java.JavaClasspath;
import org.emftext.language.java.JavaUniquePathConstructor;
import org.emftext.language.java.classifiers.Classifier;
import org.emftext.language.java.ejava.EClassifierClassWrapper;
import org.emftext.language.java.ejava.EClassifierEnumerationWrapper;
import org.emftext.language.java.ejava.EClassifierInterfaceWrapper;
import org.emftext.language.java.ejava.EClassifierWrapper;
import org.emftext.language.java.ejava.EEnumLiteralWrapper;
import org.emftext.language.java.ejava.EOperationWrapper;
import org.emftext.language.java.ejava.EPackageWrapper;
import org.emftext.language.java.ejava.EStructuralFeatureGetWrapper;
import org.emftext.language.java.ejava.EStructuralFeatureSetWrapper;
import org.emftext.language.java.ejava.EjavaFactory;
import org.emftext.language.java.generics.GenericsFactory;
import org.emftext.language.java.generics.QualifiedTypeArgument;
import org.emftext.language.java.generics.TypeParameter;
import org.emftext.language.java.members.EnumConstant;
import org.emftext.language.java.members.Method;
import org.emftext.language.java.parameters.Parameter;
import org.emftext.language.java.parameters.ParametersFactory;
import org.emftext.language.java.types.ClassifierReference;
import org.emftext.language.java.types.PrimitiveType;
import org.emftext.language.java.types.Type;
import org.emftext.language.java.types.TypeReference;
import org.emftext.language.java.types.TypesFactory;
import org.emftext.language.java.types.TypesPackage;
import org.emftext.language.java.types.Void;
/**
* Wraps an Ecore model in an eJava model. This way, the Ecore types can be
* interpreted as Java types by JaMoPP.
*/
public class EcoreWrapper {
public static void wrap(EPackageWrapper mainEPackageWrapper) {
EMap<EList<String>, GenPackage> genPackagesInScope = findGenPackagesInScope(mainEPackageWrapper);
for (final List<String> namespaces : genPackagesInScope.keySet()) {
GenPackage genPackage = genPackagesInScope.get(namespaces);
if (genPackage.eIsProxy() && genPackage.getEcorePackage().eIsProxy()) {
String message = "Can't find generator package for " + namespaces.toString();
addError(mainEPackageWrapper.eResource(), message);
continue;
}
wrapEPackage(genPackage, namespaces, mainEPackageWrapper);
}
}
private static void addError(Resource resource, final String message) {
resource.getErrors().add(new Diagnostic() {
public String getMessage() {
return message;
}
public String getLocation() {
return null;
}
public int getLine() {
return 0;
}
public int getColumn() {
return 0;
}
@Override
public String toString() {
return message;
}
});
}
public static void wrapEPackage(GenPackage genPackage, List<String> namespaces, EPackageWrapper mainEPackageWrapper) {
EPackageWrapper wrapper = null;
Resource wrapperResource = null;
JavaClasspath cp = JavaClasspath.get(mainEPackageWrapper);
String packageName = "";
for (String namespace : namespaces) {
packageName += namespace + ".";
}
if (mainEPackageWrapper.getNamespaces().equals(namespaces)) {
wrapper = mainEPackageWrapper;
wrapperResource = mainEPackageWrapper.eResource();
wrapper.setGenPackage(genPackage);
wrapper.setEPackage(genPackage.getEcorePackage());
}
for(EClassifier eClassifier : genPackage.getEcorePackage().getEClassifiers()) {
if (wrapper == null) {
URI uri = JavaUniquePathConstructor.getJavaFileResourceURI(packageName + eClassifier.getName());
ResourceSet rs = mainEPackageWrapper.eResource().getResourceSet();
wrapperResource = rs.getResource(uri, false);
if (wrapperResource == null) {
wrapperResource = new ResourceImpl(uri);
rs.getResources().add(wrapperResource);
}
else {
wrapperResource.getContents().clear();
}
wrapper = EjavaFactory.eINSTANCE.createEPackageWrapper();
wrapperResource.getContents().add(wrapper);
wrapper.getNamespaces().addAll(namespaces);
wrapper.setGenPackage(genPackage);
wrapper.setEPackage(genPackage.getEcorePackage());
}
wrapEClassifier(eClassifier, wrapper);
if (wrapperResource == null) {
// TODO log error message?
return;
}
cp.registerClassifier(packageName, eClassifier.getName(), wrapperResource.getURI());
}
}
public static void wrapEClassifier(EClassifier eClassifier, EPackageWrapper ePackageWrapper) {
EClassifierWrapper wrapper = (EClassifierWrapper) ePackageWrapper.getContainedClassifier(eClassifier.getName());
if (wrapper == null) {
if (eClassifier instanceof EClass) {
wrapper = EjavaFactory.eINSTANCE.createEClassifierInterfaceWrapper();
}
else if (eClassifier instanceof EEnum) {
wrapper = EjavaFactory.eINSTANCE.createEClassifierEnumerationWrapper();
}
else {
return;
}
wrapper.setName(eClassifier.getName());
ePackageWrapper.getClassifiers().add(wrapper);
}
wrapper.setEClassifier(eClassifier);
if (eClassifier instanceof EClass) {
EClass eClass = (EClass)eClassifier;
EList<TypeReference> superTypeList = null;
if (wrapper instanceof EClassifierInterfaceWrapper) {
superTypeList =
((EClassifierInterfaceWrapper)wrapper).getExtends();
}
else /*wrapper instanceof EClassifierClassWrapper*/ {
superTypeList =
((EClassifierClassWrapper)wrapper).getImplements();
}
for(EClass extendedEClass : eClass.getESuperTypes()) {
superTypeList.add(createTypeReferenceForEClassifier(extendedEClass, eClassifier.eResource().getResourceSet()));
}
JavaClasspath cp = JavaClasspath.get(eClassifier);
ClassifierReference eObjectRef = TypesFactory.eINSTANCE.createClassifierReference();
eObjectRef.setTarget((Classifier) cp.getClassifier("org.eclipse.emf.ecore.EObject"));
superTypeList.add(eObjectRef);
for(EStructuralFeature eStructuralFeature : eClass.getEStructuralFeatures()) {
wrapEStructuralFeatureForGet(eStructuralFeature, wrapper);
if (!eStructuralFeature.isMany() && !eStructuralFeature.isDerived()) {
wrapEStructuralFeatureForSet(eStructuralFeature, wrapper);
}
}
List<EOperation> eOperations = eClass.getEOperations();
eOperations.addAll(getMissingOperations(eClass));
for(EOperation eOperation : new ArrayList<EOperation>(eOperations)) {
wrapEOperation(eOperation, wrapper);
}
}
else if (eClassifier instanceof EEnum) {
EEnum eEnmu = (EEnum) eClassifier;
for(EEnumLiteral eEnumLiteral : eEnmu.getELiterals()) {
wrapEEnumLiteral(eEnumLiteral, (EClassifierEnumerationWrapper) wrapper);
}
}
}
/**
* Returns operations that are present in the implementation of
* the given EClass but that are not declared in the meta model.
* This is needed because the classes from the EMF GenModel package
* do contain methods that are not declared in the meta model.
*
* @param eClass
* @return
*/
private static Collection<EOperation> getMissingOperations(EClass eClass) {
Collection<EOperation> operations = new ArrayList<EOperation>();
// TODO this name check is not sufficient
if (eClass.getName().equals(GenModelPackage.eINSTANCE.getGenClass().getName())) {
EOperation getQualifiedInterfaceNameOperation = EcoreFactory.eINSTANCE.createEOperation();
getQualifiedInterfaceNameOperation.setName("getQualifiedInterfaceName");
getQualifiedInterfaceNameOperation.setEType(EcorePackage.eINSTANCE.getEString());
operations.add(getQualifiedInterfaceNameOperation);
}
if (eClass.getName().equals(GenModelPackage.eINSTANCE.getGenFeature().getName())) {
EOperation getTypeGenClassOperation = EcoreFactory.eINSTANCE.createEOperation();
getTypeGenClassOperation.setName("getTypeGenClass");
getTypeGenClassOperation.setEType(GenModelPackage.eINSTANCE.getGenClass());
operations.add(getTypeGenClassOperation);
}
return operations;
}
public static void wrapEStructuralFeatureForGet(
EStructuralFeature eStructuralFeature, EClassifierWrapper eClassifierWrapper) {
String prefix = "get";
EClassifier type = eStructuralFeature.getEType();
if (type != null && "EBoolean".equals(type.getName())) {
prefix = "is";
}
String getterName = prefix + firstToUpperCase(eStructuralFeature.getName());
Method method = eClassifierWrapper.getContainedMethod(getterName);
if (method != null && !(method instanceof EStructuralFeatureGetWrapper)) {
// can happen if a method with the same name was declared in eJava file
return;
}
EStructuralFeatureGetWrapper wrapper = (EStructuralFeatureGetWrapper) method;
if (wrapper == null) {
wrapper = EjavaFactory.eINSTANCE.createEStructuralFeatureGetWrapper();
eClassifierWrapper.getMembers().add(wrapper);
wrapper.setName(getterName);
}
wrapper.setEStructuralFeature(eStructuralFeature);
wrapper.setTypeReference(createTypeReferenceForETypedElement(eStructuralFeature));
}
public static void wrapEStructuralFeatureForSet(
EStructuralFeature eStructuralFeature, EClassifierWrapper eClassifierWrapper) {
String setterName = "set" + firstToUpperCase(eStructuralFeature.getName());
Method method = eClassifierWrapper.getContainedMethod(setterName);
if (method != null && !(method instanceof EStructuralFeatureGetWrapper)) {
// can happen if a method with the same name was declared in eJava file
return;
}
EStructuralFeatureSetWrapper wrapper = (EStructuralFeatureSetWrapper) method;
if(wrapper == null) {
wrapper = EjavaFactory.eINSTANCE.createEStructuralFeatureSetWrapper();
eClassifierWrapper.getMembers().add(wrapper);
wrapper.setName(setterName);
Parameter parameter = ParametersFactory.eINSTANCE.createOrdinaryParameter();
parameter.setName("value");
parameter.setTypeReference(createTypeReferenceForETypedElement(eStructuralFeature));
wrapper.getParameters().add(parameter);
}
wrapper.setEStructuralFeature(eStructuralFeature);
wrapper.setTypeReference(TypesFactory.eINSTANCE.createVoid());
}
public static void wrapEOperation(
EOperation eOperation, EClassifierWrapper eClassifierWrapper) {
EOperationWrapper wrapper = (EOperationWrapper)
eClassifierWrapper.getContainedMethod(eOperation.getName());
if (wrapper == null) {
wrapper = EjavaFactory.eINSTANCE.createEOperationWrapper();
wrapper.setName(eOperation.getName());
eClassifierWrapper.getMembers().add(wrapper);
}
wrapper.setEOperation(eOperation);
wrapper.setTypeReference(createTypeReferenceForETypedElement(eOperation));
// add type parameters to operation wrapper
List<TypeParameter> typeParameters = wrapper.getTypeParameters();
if (typeParameters.isEmpty()) {
for (ETypeParameter eTypeParameter : eOperation.getETypeParameters()) {
TypeParameter typeParameter = GenericsFactory.eINSTANCE.createTypeParameter();
typeParameter.setName(eTypeParameter.getName());
typeParameters.add(typeParameter);
}
}
if (wrapper.getParameters().isEmpty()) {
for(EParameter eParameter : eOperation.getEParameters()) {
Parameter parameter = ParametersFactory.eINSTANCE.createOrdinaryParameter();
parameter.setName(eParameter.getName());
parameter.setTypeReference(createTypeReferenceForETypedElement(eParameter));
wrapper.getParameters().add(parameter);
}
}
}
public static void wrapEEnumLiteral(
EEnumLiteral eEnumLiteral, EClassifierEnumerationWrapper eClassifierWrapper) {
EEnumLiteralWrapper wrapper = null;
for(EnumConstant constant : eClassifierWrapper.getConstants()) {
if (constant.getName().equals(eEnumLiteral.getName().toUpperCase())) {
wrapper = (EEnumLiteralWrapper) constant;
}
}
if (wrapper == null) {
wrapper = EjavaFactory.eINSTANCE.createEEnumLiteralWrapper();
wrapper.setName(eEnumLiteral.getName().toUpperCase());
eClassifierWrapper.getConstants().add(wrapper);
}
wrapper.setEEnumLiteral(eEnumLiteral);
}
private static EMap<EList<String>, GenPackage> findGenPackagesInScope(EPackageWrapper context) {
Resource eJavaResource = context.eResource();
if (eJavaResource == null) {
return ECollections.emptyEMap();
}
ResourceSet rs = eJavaResource.getResourceSet();
if (rs == null) {
return ECollections.emptyEMap();
}
EMap<EList<String>, GenPackage> result = new BasicEMap<EList<String>, GenPackage>();
String fileName = context.getNamespaces().get(0) + ".genmodel";
URI ecoreURI = eJavaResource.getURI().trimSegments(
1 + context.getNamespaces().size()).appendSegment(fileName);
try {
Resource genModelResource = rs.getResource(ecoreURI, true);
EcoreUtil.resolveAll(genModelResource);
} catch(Exception e) {
throw new RuntimeException(e);
}
// we need to create a copy of the list of resource, because the resource
// set is expanded while iterating over the resources.
List<Resource> copy = new ArrayList<Resource>();
copy.addAll(rs.getResources());
for(Resource r : copy) {
if (!r.getContents().isEmpty() && r.getContents().get(0) instanceof GenModel) {
GenModel genModel = (GenModel)r.getContents().get(0);
for(GenPackage genPackage : genModel.getGenPackages()) {
collectGenPackages(genPackage, new BasicEList<String>(), result);
}
for(GenPackage genPackage : genModel.getUsedGenPackages()) {
if (genPackage.eIsProxy()) {
// TODO add error
String message = "Can't find used generator package for " + genPackage.toString();
addError(context.eResource(), message);
continue;
}
collectGenPackages(genPackage, new BasicEList<String>(), result);
}
}
}
return result;
}
private static void collectGenPackages(GenPackage genPackage,
EList<String> basePackageName, EMap<EList<String>, GenPackage> result) {
basePackageName.add(genPackage.getPackageName());
result.put(ECollections.unmodifiableEList(basePackageName), genPackage);
for(GenPackage subGenPackage : genPackage.getNestedGenPackages()) {
collectGenPackages(subGenPackage, new BasicEList<String>(basePackageName), result);
}
}
private static Type getType(EClassifier eClassifier, ResourceSet resourceSet) {
if (eClassifier == null) {
return TypesFactory.eINSTANCE.createVoid();
}
String javaTypeName = null;
EPackage ePackage = eClassifier.getEPackage();
if (ePackage != null) {
if (eClassifier instanceof EDataType && !(eClassifier instanceof EEnum)) {
javaTypeName = ((EDataType) eClassifier).getInstanceTypeName();
if (javaTypeName != null && !javaTypeName.contains(".")) {
//primitive type
return (Type) TypesFactory.eINSTANCE.create(
(EClass) TypesPackage.eINSTANCE.getEClassifier(
firstToUpperCase(javaTypeName)));
}
} else if (isEClassifierFromEcorePackage(eClassifier)) {
javaTypeName = getEcoreImplementationTypeName(eClassifier);
}
else if (eClassifier instanceof EClass) {
javaTypeName = getFullPackageName(ePackage) + "." + eClassifier.getName();
}
else if (eClassifier instanceof EEnum) {
javaTypeName = getFullPackageName(ePackage) + "." + eClassifier.getName();
} else {
throw new RuntimeException("Unknown EClassifier type: " + eClassifier.getClass());
}
}
if (javaTypeName == null) {
return null;
}
JavaClasspath cp = JavaClasspath.get(resourceSet);
return (Type) cp.getClassifier(javaTypeName);
}
private static boolean isEClassifierFromEcorePackage(EClassifier eClassifier) {
return EcorePackage.eINSTANCE == eClassifier.getEPackage();
}
private static String getEcoreImplementationTypeName(EClassifier eClassifier) {
return eClassifier.getInstanceClassName();
}
private static TypeReference createTypeReferenceForEClassifier(EClassifier eClassifier, ResourceSet resourceSet) {
Type type = getType(eClassifier, resourceSet);
if (type instanceof TypeReference) {
return (TypeReference) type;
}
ClassifierReference ref = TypesFactory.eINSTANCE.createClassifierReference();
ref.setTarget((Classifier)type);
return ref;
}
private static TypeReference createTypeReferenceForETypedElement(ETypedElement eTypedElement) {
TypeReference baseTypeRef = createTypeReferenceForEClassifier(eTypedElement.getEType(), eTypedElement.eResource().getResourceSet());
if (baseTypeRef instanceof Void) {
return baseTypeRef;
}
if (!eTypedElement.isMany()) {
return baseTypeRef;
}
else {
ClassifierReference typeRef = TypesFactory.eINSTANCE.createClassifierReference();
if (java.util.Map.Entry.class.getName().equals(eTypedElement.getEType().getInstanceClassName())) {
Classifier mapType = (Classifier) JavaClasspath.get(eTypedElement).getClassifier(EMap.class.getName());
typeRef.setTarget((Classifier) mapType);
// TODO set type arguments for map
} else {
// we need to convert primitive types to their respective wrappers,
// because lists cannot have primitive types as type argument
PrimitiveType originalBaseTypeRef = null;
if (baseTypeRef instanceof PrimitiveType) {
originalBaseTypeRef = (PrimitiveType) baseTypeRef;
baseTypeRef = TypesFactory.eINSTANCE.createClassifierReference();
// we temporarily add 'originalBaseTypeRef' to the resource, because wrapPrimitiveType()
// requires the resource to work correctly
eTypedElement.eResource().getContents().add(originalBaseTypeRef);
((ClassifierReference)baseTypeRef).setTarget(((PrimitiveType)originalBaseTypeRef).wrapPrimitiveType());
eTypedElement.eResource().getContents().remove(originalBaseTypeRef);
}
Classifier listType;
listType = (Classifier) JavaClasspath.get(eTypedElement).getClassifier(EList.class.getName());
typeRef.setTarget((Classifier)listType);
QualifiedTypeArgument typeArgument = GenericsFactory.eINSTANCE.createQualifiedTypeArgument();
typeArgument.setTypeReference(baseTypeRef);
typeRef.getTypeArguments().add(typeArgument);
}
return typeRef;
}
}
private static String getFullPackageName(EPackage ePackage) {
String s = ePackage.getName();
while (ePackage.getESuperPackage() != null) {
ePackage = ePackage.getESuperPackage();
s = ePackage.getName() + "." + s;
}
return s;
}
private static String firstToUpperCase(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
}